/*
MIT License 

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов, 

https://bmstu.codes/lsx/simodo
*/

#ifndef simodo_token_Location
#define simodo_token_Location

/*! \file Location.h
    \brief Структура, определяющая координаты выделения в тексте, совместимые с LSP.

    \attention Данный файл используется для определения структур LSP в Qt-реализациях.
    Поэтому все структуры должны быть полностью определены в данном заголовочном файле!
*/

#include <cstdint>
#include <string>
#include <algorithm>

#include <cassert>

namespace simodo::inout
{
    typedef uint32_t position_line_t;
    typedef uint32_t position_character_t;

    class Position
    {
        position_line_t      _line      = 0;
        position_character_t _character = 0;

    public:
        Position() = default;
        Position(position_line_t line, position_character_t character) : _line(line), _character(character) {}

        position_line_t      line()      const { return _line; }
        position_character_t character() const { return _character; }
    };

}

inline bool operator < (const simodo::inout::Position & left, const simodo::inout::Position & right)
{
    if (left.line() == right.line())
        return left.character() < right.character();

    return left.line() < right.line();
}

inline bool operator <= (const simodo::inout::Position & left, const simodo::inout::Position & right)
{
    if (left.line() == right.line())
        return left.character() <= right.character();

    return left.line() < right.line();
}

inline bool operator > (const simodo::inout::Position & left, const simodo::inout::Position & right)
{
    if (left.line() == right.line())
        return left.character() > right.character();

    return left.line() > right.line();
}

inline bool operator >= (const simodo::inout::Position & left, const simodo::inout::Position & right)
{
    if (left.line() == right.line())
        return left.character() >= right.character();

    return left.line() > right.line();
}

inline bool operator == (const simodo::inout::Position & left, const simodo::inout::Position & right)
{ 
    return (left.line() == right.line() && left.character() == right.character());
}

inline bool operator != (const simodo::inout::Position & left, const simodo::inout::Position & right)
{
    return !(left == right);
}

namespace simodo::inout
{
    class Range
    {
        Position _start;
        Position _end;

    public:
        Range() = default;
        Range(Position start, Position end)
            : _start(start)
            , _end(end) 
        {
            assert(_start <= _end);
        }
        Range(position_line_t start_line, position_character_t start_character,
              position_line_t end_line,   position_character_t end_character)
            : _start(start_line, start_character)
            , _end(end_line, end_character)
        {
            assert(_start <= _end);
        }


        const Position & start() const { return _start; }
        const Position & end()   const { return _end; }

        void merge(const Range & r)
        {
            if (r.start() < start())
                _start = r.start();

            if (end() < r.end())
                _end = r.end();
        }

        bool contains(const Position & pos) const
        {
            return start() <= pos && pos < end();
        }
    };

}

inline bool operator == (const simodo::inout::Range & left, const simodo::inout::Range & right)
{ 
    return (left.start() == right.start() && left.end() == right.end());
}

inline bool operator != (const simodo::inout::Range & left, const simodo::inout::Range & right)
{ 
    return !(left == right);
}

namespace simodo::inout
{
    class Location
    {
        std::string _uri;
        Range       _range;

    public:
        Location() = default;
        Location(const Location &) = default;
        Location(Location &&) = default;
        Location(std::string uri) : _uri(std::move(uri)), _range(0, 0, 0, 0) {}
        Location(std::string uri, Range range) : _uri(std::move(uri)), _range(range) {}

        Location & operator=(const Location & other) = default;
        Location & operator=(Location && other) noexcept = default;

        const std::string & uri()   const { return _uri; }
        const Range &       range() const { return _range; }
    };

    inline const static Location null_location {{},{{0,0},{0,0}}};

}

inline bool operator == (const simodo::inout::Location & left, const simodo::inout::Location & right)
{ 
    return (left.uri() == right.uri() && left.range() == right.range());
}

inline bool operator != (const simodo::inout::Location & left, const simodo::inout::Location & right)
{ 
    return !(left == right);
}

#endif // simodo_token_Location
